package server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;

public class Server extends Thread{

    public static void main(String[] args) throws IOException {
        new Server().start();
    }
    /**
     * 基准路径，以这个文件夹作为系统目录
     */
    private final String BASE_PATH="C:/FileSystem";
    /**
     * 记录用户账户的文件
     */
    private final String SYSTEM_DATA="C:/FileSystem/user/dat.txt";
    /**
     * 系统默认端口
     */
    private final int PORT=6666;
    /**
     * 系统IP地址
     */
    private final String IP="127.0.0.1";
    /**
     * 记录用户账号和密码
     */
    private HashMap<String,String> users;
    /**
     * 服务器socket
     */
    private ServerSocket listenSocket;
    /**
     * 初始化，创建系统目录，如果创建失败建议将C盘更换成D盘
     * @throws IOException
     */
    Server() throws IOException{
        listenSocket=new ServerSocket(PORT);
        File file = new File(BASE_PATH);
        if(!file.exists()) {
            boolean success = file.mkdirs();
            if(!success) {
                System.out.println("系统目录创建失败");
                return;
            }
        }
        System.out.println("系统目录创建成功");
        file=new File(BASE_PATH+"/user");
        if(!file.exists()) {
            boolean success = file.mkdirs();
            if(!success) {
                System.out.println("系统目录user创建失败");
                return;
            }
        }
        System.out.println("系统文件创建成功");
        file=new File(SYSTEM_DATA);
        if(!file.exists()) {
            boolean success = file.createNewFile();
            if(!success) {
                System.out.println("系统文件创建失败");
                return;
            }
        }
        file=new File(BASE_PATH+"/home");
        if(!file.exists()){
            boolean success = file.mkdirs();
            if(!success) {
                System.out.println("系统目录创建失败");
                return;
            }
        }
        users=new HashMap<String, String>();
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(SYSTEM_DATA))));
        System.out.println("初始化用户数据");
        String line=null;
        while((line=br.readLine())!=null) {
            if(line.trim().equals(""))
                continue;
            if(line.endsWith("\n")) {
                line=line.substring(0,line.length()-1);
            }
            System.out.println(line);
            String[] datas = line.split("[ ]+");
            users.put(datas[0], datas[1]);
        }
        System.out.println("用户数据初始化完成");
    }

    @Override
    public void run() {
        while(true) {
            try {
                //等待用户连接
                Socket client = listenSocket.accept();
                System.out.println("接收到一个客户端连接");
                //交给线程管理
                new HandleThread(client).start();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    /**
     *    处理用户发来的指令的线程
     * @author Administrator
     *
     */
    class HandleThread extends Thread{
        /**
         * 读取用户指令的输入流
         */
        private InputStream istream;
        /**
         * 向用户返回结果的输出流
         */
        private OutputStream ostream;
        /**
         * 读取用户指令的输入流
         */
        private BufferedReader in;
        /**
         * 向用户返回结果的输出流
         */
        private BufferedWriter out;
        /**
         * 客户端socket
         */
        private Socket client;
        /**
         * 当前用户路径
         */
        private String path="/";
        /**
         * 初始化
         * @param client
         * @throws IOException
         */
        public HandleThread(Socket client) throws IOException {
            this.client=client;
            istream=client.getInputStream();
            ostream=client.getOutputStream();
            in = new BufferedReader(new InputStreamReader(istream));
            out=new BufferedWriter(new OutputStreamWriter(ostream));
        }
        @Override
        public void run() {
            System.out.println("线程初始化成功");
            String line=null;
            try {
                //读取用户指令
                while((line=in.readLine())!=null) {
                    //用户断开连接
                    if(line.equals("#INSTRUCT#close")) {
                        System.out.println("客户端"+client+"断开连接");
                        return;
                    }
                    runInstruct(line.trim());
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally {
                try {
                    if(client!=null)
                        client.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        /**
         * 执行用户指令
         * @param instruct
         * @throws IOException
         */
        private void runInstruct(String instruct) throws IOException {
            if(!instruct.startsWith("#INSTRUCT#")) {
                return;
            }
            instruct=instruct.substring(10,instruct.length());
            System.out.println("客户端"+client+"发来指令"+instruct+"\n");
            String[] parts = instruct.split(" ");
            int port;
            File file;
            switch(parts[0]) {
                case "signup"://注册
                    if(parts.length<2) {
                        out.write("ERROR请完整输入用户名和密码");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    if(users.containsKey(parts[1])) {
                        out.write("ERROR用户"+parts[1]+"已存在");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    writeToSystemFile(parts[1]+" "+parts[2]);
                    users.put(parts[1],parts[2]);
                    file=new File(BASE_PATH+"/home/"+parts[1]);
                    if(file.mkdirs()==false) {
                        out.write("ERROR家目录创建失败");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    path="/home/"+parts[1];
                    out.write("注册成功，已登录");
                    out.newLine();
                    out.flush();
                    break;
                case "login"://登陆
                    if(!users.containsKey(parts[1])) {
                        System.out.println("登陆失败");
                        out.write("ERROR用户"+parts[1]+"不存在");
                        out.newLine();
                        out.flush();
                    }else if(users.get(parts[1]).equals(parts[2])) {
                        out.write("登录成功");
                        out.newLine();
                        out.flush();
                        path="/home/"+parts[1];
                    }else {
                        System.out.println("登陆失败");
                        out.write("ERROR用户名或密码错误");
                        out.newLine();
                        out.flush();
                    }
                    break;
                case "sls"://查看服务器文件列表
                    file = new File(BASE_PATH+path);
                    System.out.println(BASE_PATH+path);
                    File[] files = file.listFiles();
                    for(File f:files) {
                        out.write(f.getName());
                        out.newLine();
                        out.flush();
                    }
                    out.write("#END#");
                    out.newLine();
                    out.flush();
                    break;
                case "spwd"://查看当前用户路径
                    out.write(path);
                    out.newLine();
                    out.flush();
                    break;
                case "scd"://切换目录
                    if(parts.length<2) {
                        out.write("请输入路径");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    String[] splits = parts[1].split("/");
                    if(!splits[0].equals("..")){
                        StringBuilder sb=new StringBuilder(path);
                        for(String tmp:splits) {
                            if(!tmp.trim().equals("")) {
                                sb.append("/"+tmp);
                            }
                        }
                        sb.append("/");
                        String tmppath=sb.toString();
                        System.out.println(client+"切换路径"+BASE_PATH+tmppath);
                        if(new File(BASE_PATH+tmppath).exists()==false) {
                            out.write("路径不存在"+tmppath);
                            out.newLine();
                            out.flush();
                        }else {
                            path=tmppath;
                            out.write("切换成功，当前路径："+path);
                            out.newLine();
                            out.flush();
                        }
                    }else {
                        String[] ps = path.split("/");
                        if(ps.length<splits.length) {
                            path="/";
                            out.write("切换成功，当前路径："+path);
                            out.newLine();
                            out.flush();
                            return;
                        }
                        StringBuilder sb=new StringBuilder("/"+ps[0]);
                        for(int i=1;i<ps.length-splits.length;i++) {
                            sb.append("/"+ps[i]);
                        }
                        System.out.println(client+"切换路径"+BASE_PATH+path);
                        if(new File(BASE_PATH+sb.toString()).exists()==false) {
                            out.write("切换失败，路径错误："+path);
                            out.newLine();
                            out.flush();
                            return;
                        }
                        path=sb.toString();
                        out.write("切换成功，当前路径："+path);
                        out.newLine();
                        out.flush();
                    }
                    break;
                case "smkdir"://创建目录
                    if(new File(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1])).mkdirs()==false) {
                        out.write("目录:"+(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1]))+"创建失败");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    out.write("创建成功");
                    out.newLine();
                    out.flush();
                    break;
                case "srmdir"://删除目录
                    if(new File(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1])).exists()==false) {
                        out.write("目录:"+(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1]))+"不存在");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    if(new File(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1])).delete()==false) {
                        out.write("目录:"+(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1]))+"删除失败");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    out.write("删除成功");
                    out.newLine();
                    out.flush();
                    break;
                case "put"://上传文件
                    //向客户端发送系统剩余空间大小
                    out.write(""+new File(BASE_PATH).getFreeSpace());
                    out.newLine();
                    out.flush();
                    //系统空间不够
                    if(!in.readLine().equals("enough"))
                        return;
                    //随机生成可用端口
                    port=9999;
                    while(isPortUsing(IP,port)==true) {//端口被占用
                        port=(int)(Math.random()*10000+5000);
                    }
                    new PutFileThread(new ServerSocket(port),
                            BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1])).start();
                    out.write(IP+" "+port);
                    out.newLine();
                    out.flush();
                    break;
                case "get"://下载文件
                    System.out.println(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1]));
                    file=new File(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1]));
                    if(file.exists()==false) {
                        System.out.println("文件不存在");
                        out.write("ERROR文件:"+(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1]))+"不存在");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    if(file.isDirectory()==true) {
                        System.out.println("不能get目录");
                        out.write("ERROR文件:"+(BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1]))+"为文件夹，不能下载");
                        out.newLine();
                        out.flush();
                        return;
                    }
                    //返回文件大小
                    out.write(""+file.length());
                    out.newLine();
                    out.flush();
                    //用户硬盘大小不够
                    if(!in.readLine().equals("enough")) {
                        return;
                    }
                    port=9999;
                    while(isPortUsing(IP,port)==true) {//端口被占用
                        port=(int)(Math.random()*10000+5000);
                    }

                    new GetFileThread(new ServerSocket(port),
                            BASE_PATH+path+(parts[1].startsWith("/")?parts[1]:"/"+parts[1])).start();
                    out.write(IP+" "+port);
                    out.newLine();
                    out.flush();
                    break;
                default://指令不存在
                    out.write("指令"+instruct+"不存在");
                    out.newLine();
                    out.flush();
            }
        }
    }
    /**
     * 读取用户上传文件的线程
     *
     */
    class PutFileThread extends Thread{
        /**
         * 读取用户输入的输入流
         */
        BufferedInputStream bis;
        /**
         * 存放文件的输出流
         */
        BufferedOutputStream bos;
        ServerSocket serverSocket;
        String path;
        public PutFileThread(ServerSocket serverSocket,String path) {
            System.out.println("文件路径："+path);
            this.serverSocket=serverSocket;
            this.path=path;
        }

        @Override
        public void run() {
            Socket socket = null;
            byte[] buff=new byte[1024];
            int len=0;
            try {
                socket = serverSocket.accept();
                this.bis=new BufferedInputStream(socket.getInputStream());
                this.bos=new BufferedOutputStream(new FileOutputStream(path));
                while((len=bis.read(buff))!=-1) {
                    bos.write(buff, 0, len);
                }
                bos.flush();
                System.out.println("读取完毕");
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }finally {
                try {
                    bis.close();
                    bos.close();
                    serverSocket.close();
                    socket.close();

                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 下载文件功能的线程
     *
     */
    class GetFileThread extends Thread{
        /**
         * 读取文件的输入流
         */
        BufferedInputStream bis;
        /**
         * 向客户端输出文件的输出流
         */
        BufferedOutputStream bos;
        ServerSocket serverSocket;
        String path;
        /**
         *初始化
         * @param serverSocket
         * @param path
         */
        public GetFileThread(ServerSocket serverSocket,String path) {
            System.out.println("文件路径："+path);
            this.serverSocket=serverSocket;
            this.path=path;
        }

        @Override
        public void run() {
            Socket socket = null;
            byte[] buff=new byte[1024];
            int len=0;
            try {
                socket = serverSocket.accept();
                this.bis=new BufferedInputStream(new FileInputStream(path));
                this.bos=new BufferedOutputStream(socket.getOutputStream());
                while((len=bis.read(buff))!=-1) {
                    bos.write(buff, 0, len);
                }
                bos.flush();
                System.out.println("传输完毕,文件名:"+path+" 文件大小:"+new File(path).length()/1024.0/1024.0+"M");
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }finally {
                try {
                    bis.close();
                    bos.close();
                    serverSocket.close();
                    socket.close();

                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 将新用户写入系统文件dat.txt
     * @param user
     */
    private void writeToSystemFile(String user) {
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(SYSTEM_DATA),true)));
            bw.write(user);
            bw.newLine();
            bw.flush();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            try {
                if(bw!=null)
                    bw.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    /***
     * 判断端口是否被占用
     *  true:already in using  false:not using
     * @param host
     * @param port
     * @throws UnknownHostException
     */
    @SuppressWarnings("resource")
    public static boolean isPortUsing(String host,int port) throws UnknownHostException {
        boolean flag = false;
        InetAddress theAddress = InetAddress.getByName(host);
        try {
            Socket socket = new Socket(theAddress,port);
            flag = true;
        } catch (IOException e) {

        }
        return flag;
    }


}
